package com.txw.javaCore.server.util;

import com.github.rholder.retry.*;
import com.google.common.base.Predicates;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @PROJECT_NAME: JavaCoreB2
 * @DESCRIPTION: Guava_retrying机制实现重试-入门Demo
 * 业务场景：远程调用超时、网络突然中断
 * @Author: 涂玄武
 * @DATE: 2021/3/16 14:19
 */
@Slf4j
public class RetryUtil {

    private static Integer i = 1;

    private static Integer execute() throws Exception{
        log.info("----重试时 变量i的叠加逻辑----");
        return i++;
    }

    /**
     * “重试机制”功能实现的核心在于定义Retryer实例以及Callable任务运行实例
     * Retryer实例：“什么时机重试”、“重试的次数”、“重试的时间间隔”、“每次重试时，定义Listener监听一些操作逻辑”等等
     * @param args
     */
    public static void main(String[] args) {
        //TODO:定义任务实例
        Callable<String> callable = () -> {
            Integer res = execute();
            if (res>3){
                return res.toString();
            }
            return null;
        };

        //TODO:每次重试时 监听器执行的逻辑
        RetryListener retryListener = new RetryListener() {
            @Override
            public <V> void onRetry(Attempt<V> attempt) {
                //重试次数
                long attemptNumber = attempt.getAttemptNumber();
                log.info("----每次重试时 监听器执行的逻辑,当前已经是第 {} 次重试了----",attemptNumber);

                if (attemptNumber == 3){
                    log.error("--重试次数已到，是不是得该执行一些补偿逻辑，如发送短信、发送邮件...");
                }
            }
        };

        //TODO:定义重试器
        Retryer<String> retryer = RetryerBuilder.<String>newBuilder()
                //TODO:当返回结果为Null时 - 执行重试
                .retryIfResult(Predicates.isNull())
                //TODO:当执行核心业务逻辑抛出RuntimeException - 执行重试
                .retryIfRuntimeException()
                //TODO:还可以自定义抛出何种异常时 - 执行重试
                .retryIfExceptionOfType(IOException.class)

                //TODO:每次重试时的时间间隔为5S-重试的时间间隔设置策略WaitStrategies
                .withWaitStrategy(WaitStrategies.fixedWait(5L, TimeUnit.SECONDS))
                //TODO:重试次数为3次，3次之后就不重试了-重试次数策略StopStrategies
                .withStopStrategy(StopStrategies.stopAfterAttempt(3))
                //TODO:每次重试时定义一个监听器listener，监听器的逻辑可以是 "日志记录"、"做一些补偿操作"...
                .withRetryListener(retryListener)
                .build();

        try {
            retryer.call(callable);
        } catch (ExecutionException | RetryException e ) {
            e.printStackTrace();
        }

    }

}
